﻿/* 
   Copyright 2011 LUCA Studios LLC & John Evans

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

namespace LUCAStudios.Silverlight.Controls.DragDrop
{
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;

    /// <summary>
    /// Provides a base class for DropZone elements
    /// </summary>
    public abstract class DropZoneBase
    {
        private FrameworkElement _hotSpot;
        private readonly Grid _defaultHotSpot = new Grid();

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="source"></param>
        /// <param name="dimensions"></param>
        /// <param name="dropZoneOffsets"></param>
        protected DropZoneBase(IDragDropTarget source, Rect dimensions, Thickness dropZoneOffsets)
        {
            Source = source;
            TargetDimensions = dimensions;
            DropZoneOffsets = dropZoneOffsets;
            HotSpot = _defaultHotSpot;

            Canvas.SetLeft(HotSpot, dimensions.Left);
            Canvas.SetTop(HotSpot, dimensions.Top);
            HotSpot.Width = dimensions.Width;
            HotSpot.Height = dimensions.Height;
            DropZoneUI = HotSpot;
        }

        internal static readonly Thickness NoOffsets = new Thickness(0, 0, 0, 0);

        /// <summary>
        /// Source object
        /// </summary>
        protected internal IDragDropTarget Source { get; private set; }

        /// <summary>
        /// Dimensions of the drop target
        /// </summary>
        protected Rect TargetDimensions { get; private set; }

        /// <summary>
        /// Specifies adjustments to the dropzone hotspot
        /// {Left, Top, Right, Bottom}
        /// Base class does not apply offsets.  Derived classes should do that.
        /// </summary>
        protected Thickness DropZoneOffsets { get; private set; }

        /// <summary>
        /// Called when mouse enters drop zone HotSpot element.
        /// </summary>
        protected abstract void OnDropZoneEntered();
        /// <summary>
        /// Called when mouse leaves drop zone HotSpot element.
        /// </summary>
        protected abstract void OnDropZoneLeave();
        /// <summary>
        /// Called when mouse left button state changes to 'Up' on a drop zone HotSpot element.
        /// </summary>
        protected abstract void OnDropZoneDropped();

        private void SetHotSpotEvents()
        {
            if (HotSpot == default(FrameworkElement)) return;
            HotSpot.MouseEnter += HotSpotMouseEnter;
            HotSpot.MouseLeave += HotSpotMouseLeave;
            HotSpot.MouseLeftButtonUp += HotSpotMouseLeftButtonUp;
        }

        private void ClearHotSpotEvents()
        {
            if (HotSpot == default(FrameworkElement)) return;
            HotSpot.MouseEnter -= HotSpotMouseEnter;
            HotSpot.MouseLeave -= HotSpotMouseLeave;
            HotSpot.MouseLeftButtonUp -= HotSpotMouseLeftButtonUp;
        }

        private void HotSpotMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            OnDropZoneDropped();
            if (MouseLeftButtonUp != null) MouseLeftButtonUp(this, e);
        }

        private void HotSpotMouseLeave(object sender, MouseEventArgs e)
        {
            OnDropZoneLeave();
            if (MouseLeave != null) MouseLeave(this, e);
        }

        private void HotSpotMouseEnter(object sender, MouseEventArgs e)
        {
            OnDropZoneEntered();
            if (MouseEnter != null) MouseEnter(this, e);
        }

        #region IDisposable Members

        /// <summary>
        /// Disposes of the control, removing event hooks
        /// </summary>
        public void Dispose()
        {
            ClearHotSpotEvents();
        }

        #endregion

        /// <summary>
        /// Gets the hotspot which should be used by the DragDrop framework for mouse events.
        /// </summary>
        protected internal FrameworkElement HotSpot
        {
            get { return _hotSpot; }
            set
            {
                if (value == null) return;

                if (_hotSpot != null)
                {
                    ClearHotSpotEvents();
                }
                _hotSpot = value;
                SetHotSpotEvents();
            }
        }

        /// <summary>
        /// Gets the UI element that will be added to the Visual Tree.  HotSpot should be a child of this element
        /// or should be this element.
        /// </summary>
        protected internal FrameworkElement DropZoneUI { get; protected set; }

        internal event MouseButtonEventHandler MouseLeftButtonUp;

        internal event MouseEventHandler MouseEnter;

        internal event MouseEventHandler MouseLeave;

    }
}
